package com.boot.webflux.security.example2.config.authentication;

import com.boot.webflux.security.example2.config.CloudUserDetailsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 此处用于从请求的信息中载入验证信息（即将header中的token包装成Authentication并进行验证）
 * 还有一个作用： 禁用session ,因为默认实现是 WebSessionServerSecurityContextRepository
 */
@Component
@Slf4j
public class CloudContextRepository implements ServerSecurityContextRepository {
    @Autowired
    private CloudUserDetailsService cloudUserDetailsService;

    /**
     * 在 AuthenticationWebFilter 中被调用;
     * 主要作用：在登陆成功后，设置SecurityContext
     * @param exchange
     * @param context
     * @return
     */
    @Override
    public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
        log.info("------------ContextRepository----save------: ");
        if(context ==null){
            UserDetails user =  cloudUserDetailsService.findByUsername("admin").block();
            Authentication authentication = new UsernamePasswordAuthenticationToken(user,"",user.getAuthorities() );
            SecurityContext securityContext = new SecurityContextImpl(authentication);
            return Mono.empty().then().subscriberContext( ReactiveSecurityContextHolder.withSecurityContext( Mono.just(securityContext) ) );
        }
        return  Mono.empty().then().subscriberContext( ReactiveSecurityContextHolder.withSecurityContext( Mono.just(context) ));
    }

    /**
     *
     * 在 ReactorContextWebFilter( AuthenticationWebFilter 的上一个 filter ) 中被调用;
     * 默认实现是： WebSessionServerSecurityContextRepository； 获取对应 session 中的 SecurityContext 返回，若session==null || SecurityContext==null,则返回空。
     * 主要作用：获取 SecurityContext
     * 注意 ：这里可以使用 redis key 代替 session ，使其适用于分布式环境，
     * 一个客户端一个session，session 里保存 SecurityContext 对象; 一个用户一个 redis key ，redis key 也保存SecurityContext 对象
     *
     * @param exchange
     * @return
     */
    @Override
    public Mono<SecurityContext> load(ServerWebExchange exchange) {
        log.info("------------ContextRepository----load------");
        String requestTokenHeader = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (requestTokenHeader == null || !requestTokenHeader.startsWith("Bearer ")) {
            return Mono.empty();
        }
        String requestToken = requestTokenHeader.substring(7);
        if(!requestToken.startsWith("token")){
            return Mono.empty();
        }
        UserDetails user =  cloudUserDetailsService.findByUsername("admin").block();
        Authentication authentication = new UsernamePasswordAuthenticationToken(user,"",user.getAuthorities() );
        SecurityContext context = new SecurityContextImpl(authentication);
        return Mono.just(context);


    }
}
